package com.andova.test

import android.content.Context
import android.graphics.BitmapFactory
import android.opengl.GLES30
import android.opengl.GLUtils
import androidx.annotation.RawRes
import com.andova.cameraview.R
import com.andova.ext.getAsset
import com.andova.ext.i
import com.andova.image.glesInitialize
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.ByteOrder

@Deprecated("just for test")
class SampleTexture(private val context: Context) {
    private var program1 = 0
    private var program2 = 0
    private var texIdArray = IntArray(2)

    @Deprecated("just for test")
    fun init() {
        i("${Thread.currentThread()} SampleTexture init")
        program1 = glesInitialize(getAsset(context, "v_simple_texture.glsl"), getAsset(context, "f_simple_texture.glsl"))
        program2 = glesInitialize(getAsset(context, "v_simple_texture.glsl"), getAsset(context, "f_simple_texture.glsl"))

        val tex1 = genTexture(R.raw.wall)
        val tex2 = genTexture(R.raw.robot)
        texIdArray[0] = tex1
        texIdArray[1] = tex2
    }

    private fun genTexture(@RawRes raw: Int): Int {
        val texIds = IntArray(1)
        GLES30.glGenTextures(texIds.size, texIds, 0)
        texImage2D(texIds[0], raw)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)
        return texIds[0]
    }

    private fun texImage2D(texId: Int, @RawRes raw: Int) {
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)

        val instr = context.resources.openRawResource(raw)
        val bmp = try {
            BitmapFactory.decodeStream(instr)
        } catch (e: IOException) {
            null
        } finally {
            try {
                instr.close()
            } catch (t: IOException) {
            }
        }
        bmp ?: return
        GLUtils.texImage2D(
            GLES30.GL_TEXTURE_2D,   // 纹理类型
            0,                 // 纹理的层次
            bmp,                    // 纹理图像
            0                // 纹理边框尺寸
        )
        bmp.recycle()
    }

    fun textureId() = texIdArray[0]

    @Deprecated("just for test")
    fun destroyed() {
        i("${Thread.currentThread()} SampleTexture destroyed")
        GLES30.glDeleteTextures(texIdArray.size, texIdArray, 0)
    }

    // todo 为什么必须全部使用GL_TEXTURE0才不会报错呢
    @Deprecated("just for test")
    fun drawAllTexture() {
        drawTextureWithFullScreen(texIdArray[1], 0)
        drawTextureWithFullScreen(texIdArray[0], 0, 1f)
    }

    @Deprecated("just for test")
    fun drawTextureWithFullScreen(texId: Int = texIdArray[0], texIndex: Int = 0, z: Float = 0f) {
        val vertices: FloatArray = floatArrayOf(
            -1f, -1f, z,
            1f, -1f, z,
            1f, 1f, z,
            -1f, 1f, z
        )
        val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertices).position(0)
        val textures: FloatArray = floatArrayOf(
            0f, 1f,
            1f, 1f,
            1f, 0f,
            0f, 0f
        )
        val textureBuffer = ByteBuffer.allocateDirect(textures.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textures).position(0)

        val program = when (texId) {
            texIdArray[0] -> {
                GLES30.glViewport(200, 500, 400, 300)
                program1
            }
            texIdArray[1] -> {
                GLES30.glViewport(0, 0, 1080, 1920)
                program2
            }
            else -> return
        }
        GLES30.glUseProgram(program)
        val matrix = floatArrayOf(
            1f, 0f, 0f, 0f,
            0f, 1f, 0f, 0f,
            0f, 0f, 1f, 0f,
            0f, 0f, 0f, 1f
        )
        val attrPosition = GLES30.glGetAttribLocation(program, "aPosition")
        val attrTexCoordinate = GLES30.glGetAttribLocation(program, "aTexCoordinate")
        val unifMVPMatrix = GLES30.glGetUniformLocation(program, "uMVPMatrix")
        GLES30.glUniformMatrix4fv(unifMVPMatrix, 1, false, matrix, texIndex)
        GLES30.glVertexAttribPointer(attrPosition, 3, GLES30.GL_FLOAT, false, 3 * 4, vertexBuffer)
        GLES30.glVertexAttribPointer(attrTexCoordinate, 2, GLES30.GL_FLOAT, false, 2 * 4, textureBuffer)
        GLES30.glEnableVertexAttribArray(attrPosition)
        GLES30.glEnableVertexAttribArray(attrTexCoordinate)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0 + texIndex)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId)
//        val count = 1
//        if (rawIndex % count == 0) texImage2D(texId, rawArray[rawIndex / count])
//        if (rawIndex / count + 1 >= rawArray.size) rawIndex = 0 else ++rawIndex
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)
    }

    @Deprecated("just for test")
    fun drawTextureWithViewport(x: Int, y: Int, width: Int, height: Int) {
        val vertices: FloatArray = floatArrayOf(
            -1f, -1f, 0f,
            1f, -1f, 0f,
            1f, 1f, 0f,
            -1f, 1f, 0f
        )
        val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertices).position(0)
        val textures: FloatArray = floatArrayOf(
            0f, 1f,
            1f, 1f,
            1f, 0f,
            0f, 0f
        )
        val textureBuffer = ByteBuffer.allocateDirect(textures.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textures).position(0)

        GLES30.glUseProgram(program1)
        GLES30.glViewport(x, y, width, height)
        val matrix = floatArrayOf(
            1f, 0f, 0f, 0f,
            0f, 1f, 0f, 0f,
            0f, 0f, 1f, 0f,
            0f, 0f, 0f, 1f
        )
        val attrPosition = GLES30.glGetAttribLocation(program1, "aPosition")
        val attrTexCoordinate = GLES30.glGetAttribLocation(program1, "aTexCoordinate")
        val unifMVPMatrix = GLES30.glGetUniformLocation(program1, "uMVPMatrix")
        GLES30.glUniformMatrix4fv(unifMVPMatrix, 1, false, matrix, 0)
        GLES30.glVertexAttribPointer(attrPosition, 3, GLES30.GL_FLOAT, false, 3 * 4, vertexBuffer)
        GLES30.glVertexAttribPointer(attrTexCoordinate, 2, GLES30.GL_FLOAT, false, 2 * 4, textureBuffer)
        GLES30.glEnableVertexAttribArray(attrPosition)
        GLES30.glEnableVertexAttribArray(attrTexCoordinate)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texIdArray[0])
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)
    }

    @Deprecated("just for test")
    fun drawTextureWithFrameBuffer(texId: Int = texIdArray[0]) {
        val fbo = IntArray(1)
        val rbo = IntArray(1)
        GLES30.glGenFramebuffers(fbo.size, fbo, 0)
        GLES30.glGenRenderbuffers(rbo.size, rbo, 0)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0])
        GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, rbo[0])
        GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_DEPTH_COMPONENT16, 400, 300)
        GLES30.glFramebufferRenderbuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_DEPTH_ATTACHMENT, GLES30.GL_RENDERBUFFER, rbo[0])
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, texIdArray[0], 0)

        GLES30.glEnable(GLES30.GL_DEPTH_TEST)
        GLES30.glViewport(200, 500, 400, 300)
        val vertices: FloatArray = floatArrayOf(
            -1f, -1f, 2f,
            1f, -1f, 2f,
            1f, 1f, 2f,
            -1f, 1f, 2f
        )
        val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertices).position(0)
        val textures: FloatArray = floatArrayOf(
            0f, 1f,
            1f, 1f,
            1f, 0f,
            0f, 0f
        )
        val textureBuffer = ByteBuffer.allocateDirect(textures.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textures).position(0)

        val program = when (texId) {
            texIdArray[0] -> program1
            texIdArray[1] -> program2
            else -> return
        }
        GLES30.glUseProgram(program)
        val matrix = floatArrayOf(
            0.7f, 0f, 0f, 0f,
            0f, 0.7f, 0f, 0f,
            0f, 0f, 1f, 0f,
            0f, 0f, 0f, 1f
        )
        val attrPosition = GLES30.glGetAttribLocation(program, "aPosition")
        val attrTexCoordinate = GLES30.glGetAttribLocation(program, "aTexCoordinate")
        val unifMVPMatrix = GLES30.glGetUniformLocation(program, "uMVPMatrix")
        GLES30.glUniformMatrix4fv(unifMVPMatrix, 1, false, matrix, 0)
        GLES30.glVertexAttribPointer(attrPosition, 3, GLES30.GL_FLOAT, false, 3 * 4, vertexBuffer)
        GLES30.glVertexAttribPointer(attrTexCoordinate, 2, GLES30.GL_FLOAT, false, 2 * 4, textureBuffer)
        GLES30.glEnableVertexAttribArray(attrPosition)
        GLES30.glEnableVertexAttribArray(attrTexCoordinate)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)

        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)
        GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, GLES30.GL_NONE)
        GLES30.glDeleteFramebuffers(fbo.size, fbo, 0)
        GLES30.glDeleteRenderbuffers(rbo.size, rbo, 0)
    }
}